package main

import (
	"testing"
)

// TestVectorProcedures tests the vector procedures
// implemented in builtins.go according to R4RS section 6.6
func TestVectorProcedures(t *testing.T) {
	env := NewGlobalEnvironment()

	// Test cases for vector?
	vectorPredicates := []struct {
		input    string
		expected string
	}{{
		input:    "(vector? #(1 2 3))",
		expected: "#t",
	}, {
		input:    "(vector? '())",
		expected: "#f",
	}, {
		input:    "(vector? #t)",
		expected: "#f",
	}, {
		input:    "(vector? 42)",
		expected: "#f",
	}}

	// Run vector? tests
	runTests(t, env, vectorPredicates)

	// Test cases for make-vector
	makeVectorTests := []struct {
		input    string
		expected string
	}{{
		input:    "(vector-length (make-vector 0))",
		expected: "0",
	}, {
		input:    "(vector-length (make-vector 5))",
		expected: "5",
	}, {
		input:    "(vector-ref (make-vector 5 'a) 2)",
		expected: "a",
	}, {
		input:    "(vector-ref (make-vector 5 42) 3)",
		expected: "42",
	}}

	// Run make-vector tests
	runTests(t, env, makeVectorTests)

	// Test cases for vector
	vectorTests := []struct {
		input    string
		expected string
	}{{
		input:    "(vector)",
		expected: "#()",
	}, {
		input:    "(vector 1 2 3)",
		expected: "#(1 2 3)",
	}, {
		input:    "(vector 'a 'b 'c)",
		expected: "#(a b c)",
	}, {
		input:    "(vector 1 #t \"string\")",
		expected: "#(1 #t \"string\")",
	}}

	// Run vector tests
	runTests(t, env, vectorTests)

	// Test cases for vector-length
	vectorLengthTests := []struct {
		input    string
		expected string
	}{{
		input:    "(vector-length #())",
		expected: "0",
	}, {
		input:    "(vector-length #(1 2 3 4 5))",
		expected: "5",
	}, {
		input:    "(vector-length (make-vector 10))",
		expected: "10",
	}}

	// Run vector-length tests
	runTests(t, env, vectorLengthTests)

	// Test cases for vector-ref
	vectorRefTests := []struct {
		input    string
		expected string
	}{{
		input:    "(vector-ref #(1 2 3 4 5) 0)",
		expected: "1",
	}, {
		input:    "(vector-ref #(1 2 3 4 5) 2)",
		expected: "3",
	}, {
		input:    "(vector-ref #(1 2 3 4 5) 4)",
		expected: "5",
	}, {
		input:    "(let ((v (vector 'a 'b 'c))) (vector-ref v 1))",
		expected: "b",
	}}

	// Run vector-ref tests
	runTests(t, env, vectorRefTests)

	// Test cases for vector-set!
	vectorSetTests := []struct {
		input    string
		expected string
	}{{
		input:    "(let ((v (vector 1 2 3))) (vector-set! v 1 99) v)",
		expected: "#(1 99 3)",
	}, {
		input:    "(let ((v (make-vector 3))) (vector-set! v 0 'a) (vector-set! v 1 'b) (vector-set! v 2 'c) v)",
		expected: "#(a b c)",
	}}

	// Run vector-set! tests
	runTests(t, env, vectorSetTests)

	// Test cases for vector->list
	vectorToListTests := []struct {
		input    string
		expected string
	}{{
		input:    "(vector->list #())",
		expected: "()",
	}, {
		input:    "(vector->list #(1 2 3))",
		expected: "(1 2 3)",
	}, {
		input:    "(vector->list #(a b c))",
		expected: "(a b c)",
	}}

	// Run vector->list tests
	runTests(t, env, vectorToListTests)

	// Test cases for list->vector
	listToVectorTests := []struct {
		input    string
		expected string
	}{{
		input:    "(list->vector '())",
		expected: "#()",
	}, {
		input:    "(list->vector '(1 2 3))",
		expected: "#(1 2 3)",
	}, {
		input:    "(list->vector '(a b c))",
		expected: "#(a b c)",
	}}

	// Run list->vector tests
	runTests(t, env, listToVectorTests)

	// Test cases for vector-fill!
	vectorFillTests := []struct {
		input    string
		expected string
	}{{
		input:    "(let ((v (vector 1 2 3 4 5))) (vector-fill! v 0) v)",
		expected: "#(0 0 0 0 0)",
	}, {
		input:    "(let ((v (make-vector 3))) (vector-fill! v 'x) v)",
		expected: "#(x x x)",
	}, {
		input:    "(let ((v (vector 1 2 3 4 5))) (vector-fill! v 'a) (vector-ref v 2))",
		expected: "a",
	}}

	// Run vector-fill! tests
	runTests(t, env, vectorFillTests)

	// Test edge cases
	edgeCaseTests := []struct {
		input    string
		expected string
	}{{
		// Vectors can contain other vectors
		input:    "(let ((v (vector (vector 1 2) (vector 3 4)))) (vector-ref (vector-ref v 0) 1))",
		expected: "2",
	}, {
		// Complex nested operations
		input:    "(vector-ref (list->vector (vector->list #(1 2 3 4 5))) 3)",
		expected: "4",
	}}

	// Run edge case tests
	runVectorTests(t, env, edgeCaseTests)
}

// Helper function to run test cases
func runVectorTests(t *testing.T, env *Environment, tests []struct{ input, expected string }) {
	for _, tc := range tests {
		result, err := EvalString(tc.input, env)
		if err != nil {
			t.Errorf("Error evaluating '%s': %v", tc.input, err)
			continue
		}
		if result.String() != tc.expected {
			t.Errorf("Expected '%s' for input '%s', got '%s'", tc.expected, tc.input, result.String())
		}
	}
}
